#pragma once

#include <cstdint>
#include <map>
#include <memory>
#include <iostream>

#include "user.h"
#include "db.h"

class session;

/* TODO: 全局唯一，实现为单例，并在软件启动时初始化 */
/* 管理在线的用户 */
class user_manager {
public:
    user_manager(const user_manager &) = delete;
    user_manager &operator=(const user_manager &) = delete;

    static user_manager& get_instance()
    {
        static user_manager instance;
        return instance;
    }

    int user_login(std::shared_ptr<session> session_ptr,
        const std::string &name, const std::string &password, uint32_t &uid);
    int user_logout(uint32_t uid);

    int user_signup(const std::string &name, const std::string &password, uint32_t &uid);

    int user_delete_account(uint32_t uid);
    int user_delete_account(const std::string &name);

    uint32_t online_users_num() const
    {
        return online_users_.size();
    }

    std::shared_ptr<user> get_user(uint32_t uid)
    {
        return online_users_[uid];
    }

    int get_uid(const std::string &name, uint32_t &uid)
    {
        int ret = NET_EC_OK;
        mysqlx::RowResult res = table_.select("uid", "uname", "passwd")
                                        .where("uname = :param")
                                        .bind("param", name)
                                        .execute();
        
        if (res.count() == 0) {
            std::cout << "user not found, uname: " << name << std::endl;
            return NET_EC_NOT_FOUND;
        }

        if (res.count() > 1) {
            std::cout << "duplicate user, uname: " << name << std::endl;
            return NET_EC_DUPLICATE;
        }

        mysqlx::Row row = res.fetchOne();
        uid = row[0].get<uint32_t>();
        return ret;
    }

    const std::map<uint32_t, std::shared_ptr<user>> &online_users() const
    {
        return online_users_;
    }

    /* @return: nullptr -- not found, other -- found. */
    std::shared_ptr<session> get_session_by_uid(uint32_t uid)
    {
        auto ret = online_users_.find(uid);
        if (ret == online_users_.end()) {
            return nullptr;
        }

        auto user_ptr = online_users_[uid];

        auto ret1 = user_session_map_.find(user_ptr);
        if (ret1 == user_session_map_.end()) {
            return nullptr;
        }

        return user_session_map_[user_ptr];
    }

    std::shared_ptr<user> get_user_by_session(std::shared_ptr<session> session_ptr)
    {
        auto ret = session_user_map_.find(session_ptr);
        if (ret == session_user_map_.end()) {
            return nullptr;
        }

        auto user_ptr = session_user_map_[session_ptr];
        return user_ptr;
    }

private:
    user_manager() :
        db_(db::get_instance()),
        table_(db_.get_table("user_info"))
    {

    }

    std::map<uint32_t, std::shared_ptr<user>> online_users_;    /* key: uid, value: user_ptr */
    std::map<std::shared_ptr<session>, std::shared_ptr<user>> session_user_map_; /* key: session_ptr, value: user_ptr */
    std::map<std::shared_ptr<user>, std::shared_ptr<session>> user_session_map_; /* key: user_ptr, value: session_ptr */
    db &db_;
    mysqlx::Table table_;
};
